#pragma once

#include "common.h"
#include "opcode.h"

typedef void (*emu_port_handler_t)(uint16_t port, uint16_t *data, uint8_t data_bytes, bool input);
typedef void (*emu_intr_handler_t)(uint8_t port);

#define REG_AX 0
#define REG_CX 1
#define REG_DX 2
#define REG_BX 3
#define REG_SP 4
#define REG_BP 5
#define REG_SI 6
#define REG_DI 7

#define REG_AL 0
#define REG_CL 1
#define REG_DL 2
#define REG_BL 3
#define REG_AH 4
#define REG_CH 5
#define REG_DH 6
#define REG_BH 7

#define SEG_ES 0
#define SEG_CS 1
#define SEG_SS 2
#define SEG_DS 3

typedef struct {
    uint16_t regs[8];
    uint16_t segs[4];
    uint16_t ip;
    union {
        uint16_t data;
        struct {
            uint8_t CF : 1;
            uint8_t unused4 : 1;
            uint8_t PF : 1;
            uint8_t unused3 : 1;
            uint8_t AF : 1;
            uint8_t unused2 : 1;
            uint8_t ZF : 1;
            uint8_t SF : 1;
            uint8_t TF : 1;
            uint8_t IF : 1;
            uint8_t DF : 1;
            uint8_t OF : 1;
            uint8_t unused : 5;
        } __attribute__((packed)) bits;
    } flags;
    uint8_t *memory;
    emu_port_handler_t ports[255];
    emu_intr_handler_t intrs[255];
} emu_cpu_t;

void emu_cpu_init(emu_cpu_t *cpu, uint32_t mem_size, uint16_t base_ip);
void emu_cpu_dump(emu_cpu_t *cpu);
void emu_cpu_execute(emu_cpu_t *cpu);